#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h> 
#include <syslog.h>

#include "lock_set.c"

#define PORT 4321 //端口号
#define BUFFER_SIZE 1024  //缓存
#define MAX_QUE_CONN_NM 5 //最大连接数
#define MAX 50 //缓存题目最大题号
#define File_src "test.csv" //题目的路径
#define MESSAGE_FILE "UserMessage" //保存用户信息的路径
#define error_print "/var/log/messages" //错误存放地址




struct sockaddr_in server_sockaddr, client_sockaddr; //定义服务器地址与单个客户端地址 
int sin_size, recvbytes, sendbytes; //接受信息与发送信息状态
int accept_status[MAX_QUE_CONN_NM]; //代表这个请求的是否可以连接
int accept_num;
pthread_t thread[2*MAX_QUE_CONN_NM]; // 每一个客户端的读写(写有可能)线程
int thread_num; // 线程的数量
int sockfd, client_fd[MAX_QUE_CONN_NM]; //定义 sockfd MAX_QUE_CONN_NM客户端标识符
char buf[BUFFER_SIZE]; // 定义要发送的信息
int self_problem[MAX_QUE_CONN_NM][MAX];//客户端的每个人的题目序号
char answer[MAX]; // 缓存答案
int problem_num = 0; //共有题数
char problem[MAX][BUFFER_SIZE]; //题目缓存

char get_buf[BUFFER_SIZE]; // 得到客户的消息
char server_status; // 用来判断s是不是要退出
int testing_num,want_test_num;//在线人数和准备人数
char chLine[5] = "\n"; //写文件是使用
char blank[5] = "     "; //写文件是使用

int exit_status = 0; //服务器退出状态

/**
 * data：12/20 20:14
 * 任务开个线程监视要进行的连接的多个客户端
*/
//结构体为了在有限的连接池中可以回收连接资源
struct fd
{
    int fd_client; //连接标识符
    int flags; // 占用的连接序号
};


void *get_client(void *arg);
/*跑线程进行多个客户端接受请求*/
void *thread_accpt(void *arg)
{
    sin_size = sizeof(client_sockaddr);
    int res; //线程表示符
    while (1)
    {
        int a =0;
        //判断MAX_CONN那个可以连接
        while (1)
        {
            //获取可用的线程
            if(accept_status[a] == 0)
            {
                
                break;
            }
            else
            {
                a++;
            }
            if(a == MAX_QUE_CONN_NM)
            {
                a=0;
            }
        }
        //得到这个客户端的client_fd的标识符
         if((client_fd[a] = accept(sockfd, (struct sockaddr *)&client_sockaddr, &sin_size))==-1)
        {
            perror("accept");
            break;
        }
        accept_status[a] = 1; //变成不用状态
        printf("创建accept %d\n", a);
        /*创建读进程*/
        struct fd C_fd;
        C_fd.fd_client = client_fd[a];
        C_fd.flags = a;
        res = pthread_create(&thread[thread_num], NULL, get_client, &C_fd);
        if(res != 0)
        {
            perror("pthread");
            syslog(LOG_ERR, "pthread");
        }
        else
        {
            thread_num++;
        }
    }
}


/*进行服务端的倒计时*/
int server_down()
{
	int second =10;
	while(second >= 0)
	{
		printf("正式考试倒计时：%d s\n",second);
		second--; //减一秒
		sleep(1);
	}
	printf("现在正式考试\n");
    return 1;
}

//发送服务器的输入消息
void send_all_buf(char *buf_server);
/*开个线程进行输入*/
void *send_scanf(void *arg)
{
    while(1)
    {
        char exit_send[50]; // 发送exit
        scanf("%s",exit_send);
        if(strncmp(exit_send, "exit", 4) == 0)
        {
            send_all_buf(exit_send);
            exit_status = 1;
            break;
        }
        else
        {
            char server_send_nuf[256];
            sprintf(server_send_nuf, "服务器发来：%s", exit_send);
            send_all_buf(server_send_nuf);
        }       

    }

}

//向全部客户端发送在线人数
void send_all_client();

/*跑个线程进行阻塞读取客户端发的信息*/
void *get_client(void *arg)
{
    struct fd client_get_fd = *((struct fd *)arg); // 客户端连接的标识符
    char client_answer[MAX];//客户端的答案
    int client_answer_num=0;
    int grade = -1; //客户端用户的成绩 -1代表没有保存成绩
    char student_name[15]; //客户端学生的姓名
    char student_num[15]; //客户端的学生的学号
    int error=0;//客户端发来的异常退出次数
    int start_status = 0;//开始状态
    char frist_news[128];
    //准备人数和在线人数都不算这个客户端
    sprintf(frist_news,"status：现在在线人数%d,已经考试人数%d，准备人数%d",testing_num+want_test_num+1,testing_num,want_test_num);
    if((sendbytes = (send(client_get_fd.fd_client, frist_news, BUFFER_SIZE, 0) )) == -1)
    {
        perror("send_frist");
        syslog(LOG_ERR, "send_frist");
    }
    //服务器连上了
    if((sendbytes = (send(client_get_fd.fd_client,"success", BUFFER_SIZE, 0) )) == -1)
    {
        perror("send_client_success");
        syslog(LOG_ERR, "send_client_success");
    }
    want_test_num++;
    
    //发送学号
    if((sendbytes = (send(client_get_fd.fd_client,"student_num", BUFFER_SIZE, 0) )) == -1)
    {
        perror("send_student_num");
        syslog(LOG_ERR, "send_student_num");
    }
    /*循环获取客户端信息*/
    while (1)
    {
        memset(get_buf, 0, BUFFER_SIZE);
        /*代表读取数据有误*/
        if((recvbytes = recv(client_get_fd.fd_client, get_buf, BUFFER_SIZE, 0)) == -1)
        {
            perror("recv");
            syslog(LOG_ERR, "recv");
            break;
        }
        //对消息的处理
        //这是服务器没有准备的状态
        if(start_status == 0)
        {
             //获取字符串中包含的名字
            if(strncmp(get_buf,"student_num", strlen("student_num")) == 0)
            {
                //获取字符串中包含的学号
                strncpy(student_num,get_buf+strlen("student_num"),strlen(get_buf)-strlen("student_num")); 
                char send_client_problem[256] = "student_name";
                if((sendbytes = (send(client_get_fd.fd_client,send_client_problem, BUFFER_SIZE, 0) )) == -1)
                {
                    perror("send_student_name");
                    syslog(LOG_ERR, "send_student_name");
                }
                
            }
            //获取字符串中包含的名字
            else if(strncmp(get_buf,"student_name", strlen("student_name")) == 0)
            {
                strncpy(student_name,get_buf+strlen("student_name"),strlen(get_buf)-strlen("student_name"));

                //保存学生信息
                {
                    int fd = open(MESSAGE_FILE, O_RDWR|O_CREAT|O_APPEND, 0666);
                    lock_set(fd, F_WRLCK);
                    write(fd,student_num,strlen(student_num));
	                write(fd,blank,5);
                    write(fd,student_name,strlen(student_name));
	                write(fd,chLine,2);
                    lock_set(fd, F_UNLCK);
	                close(fd);
                }
                char send_client_problem[256] = "time_down";
                if((sendbytes = (send(client_get_fd.fd_client,send_client_problem, BUFFER_SIZE, 0) )) == -1)
                {
                    perror("time_down");
                    syslog(LOG_ERR, "time_down");
                }
                start_status = server_down();
                testing_num++;
                want_test_num--;
                /*服务器准备好发送题目*/
                {
                    char send_client_problem[BUFFER_SIZE];
                    sprintf(send_client_problem,"problem%s",problem[client_answer_num]);
                    if((sendbytes = (send(client_get_fd.fd_client,send_client_problem, BUFFER_SIZE, 0) )) == -1)
                    {
                        perror("send_client_problem");
                        syslog(LOG_ERR, "send_client_problem");
                    }
                }


            }
        }


        //对请求问题的回复并是在这是服务器准备的状态
        if (start_status == 1)
        {
        
            if(strcmp(get_buf,"problem") == 0)
            {
                char send_client_problem[BUFFER_SIZE];
                sprintf(send_client_problem,"problem%s",problem[client_answer_num]);
                if((sendbytes = (send(client_get_fd.fd_client,send_client_problem, BUFFER_SIZE, 0) )) == -1)
                {
                    perror("send_client_problem");
                    syslog(LOG_ERR, "send_client_problem");
                }
            }

            //对答案的处理
            if(strncmp(get_buf,"answer",6) == 0)
            {
                //获取答案
                client_answer[client_answer_num] = get_buf[6];
                client_answer_num++;
                /*判断是不是最大题目*/
                if(client_answer_num >= problem_num -1)
                {
                    char send_client_problem[BUFFER_SIZE];
                    sprintf(send_client_problem,"problemover");
                    if((sendbytes = (send(client_get_fd.fd_client,send_client_problem, BUFFER_SIZE, 0) )) == -1)
                    {
                        perror("sproblemover");
                        syslog(LOG_ERR, "sproblemover");
                    }

                }
                /*服务器发送下一道题目题目*/
                else
                {
                    char send_client_problem[BUFFER_SIZE];
                    sprintf(send_client_problem,"problem%s",problem[client_answer_num]);
                    if((sendbytes = (send(client_get_fd.fd_client,send_client_problem, BUFFER_SIZE, 0) )) == -1)
                    {
                        perror("send_client_problem");
                        syslog(LOG_ERR, "send_client_problem");
                    }
                }
                
            }
            // 对异常操作的处理
            else if(strcmp(get_buf, "error")==0)
            {
                char error_active[256];//错误操作的提醒
                error++;
                if(error==3)
                {
                    sprintf(error_active,"exit");
                    if((sendbytes = (send(client_get_fd.fd_client,error_active,256, 0) )) == -1)
                    {
                        perror("exit_error");
                        syslog(LOG_ERR, "exit_error");
                    }
                    break;
                }
                else
                {
                    sprintf(error_active,"status%d错误操作，回复错误操作失败", error);
                    if((sendbytes = (send(client_get_fd.fd_client,error_active,256, 0) )) == -1)
                    {
                        perror("send");
                        syslog(LOG_ERR, "send_error");
                    }
                }
            }

            //对保存成绩的处理
            else if(strcmp(get_buf,"save") == 0)
            {
                grade =0;
                char student_grade[40];
                sprintf(student_grade,"./msgsave_%s", student_num);
                int fd_grade;
                fd_grade = open(student_grade, O_RDWR | O_CREAT, 0644);
                if(fd_grade< 0)
                {
                    printf("Open file error\n");
                    exit(1);
                }
                /* 给文件上写入锁*/
                lock_set(fd_grade, F_WRLCK);
                write(fd_grade,student_num,strlen(student_num));
	            write(fd_grade,blank,5);
	            write(fd_grade,chLine,2);
                char grade_ch[40];
                for(int i=0; i<client_answer_num;i++) //判断成绩对错
                {
                    memset(grade_ch, 0, strlen(grade_ch));
                    if(client_answer[i] == answer[i]) //成绩正确
                    {
                        grade++;
                        sprintf(grade_ch, "%c   True",client_answer[i]);
                        write(fd_grade,grade_ch,strlen(grade_ch));
                        write(fd_grade,chLine,1);
                    }
                    else
                    {
                        sprintf(grade_ch, "%c   False",client_answer[i]);
                        write(fd_grade,grade_ch,strlen(grade_ch));
                        write(fd_grade,chLine,1);
                    }
                    
                }
                memset(grade_ch, 0, strlen(grade_ch));
                sprintf(grade_ch,"你的最终成绩%d\n",grade);
                write(fd_grade,grade_ch,strlen(grade_ch));
                lock_set(fd_grade, F_UNLCK);
                close(fd_grade);
                printf("你的成绩是%d\n",grade);
            }
            //对 查看的成绩的处理
            else if(strcmp(get_buf,"load")==0)
            {
                char send_grade[256];
                if(grade == -1)
                {
                    sprintf(send_grade,"你还没有保存成绩");
                }
                else
                {
                    sprintf(send_grade,"你的成绩是%d",grade);	
                }
                if((sendbytes = (send(client_get_fd.fd_client, send_grade, BUFFER_SIZE, 0) )) == -1)
                {
                    perror("send_grade");
                    syslog(LOG_ERR, "send_grade");
                }
            }
            // 对正常退出的处理
            else if(strcmp(get_buf, "exit") == 0) //退出线程
            {
                if(grade == -1)
                {
                    char *NO_SAVE = "请先保存成绩";
                    if((sendbytes = (send(client_get_fd.fd_client, NO_SAVE, BUFFER_SIZE, 0) )) == -1)
                    {
                        perror("send_exit"); 
                        syslog(LOG_ERR, "send_exit");
                    }
                }
                else
                {
                    char *NO_SAVE = "exit";
                    if((sendbytes = (send(client_get_fd.fd_client, NO_SAVE, BUFFER_SIZE, 0) )) == -1)
                    {
                        perror("client_exit");
                        syslog(LOG_ERR, "client_exit");
                    }
                    break;
                } 
            } 
        }
        //strcpy(buf, get_buf); //使主函数得到得到的字符串
    }
     accept_status[client_get_fd.flags] = 0; //释放连接状态
     testing_num--;
     send_all_client(); //向全部客户端发送在线人数
     pthread_exit(NULL);
}

//向全部客户端发送在线人数
void send_all_client()
{
    char client_num_status_buf[1024]; //在线人数和准备人数
    for(int i=0;i<MAX_QUE_CONN_NM;i++)
    {
        if(accept_status[i]==1)
        {
            printf("%d\n",i);
            sprintf(client_num_status_buf,"status：现在在线人数%d,已经考试人数%d，准备人数%d\n",testing_num+want_test_num,testing_num,want_test_num);
            if((sendbytes = (send(client_fd[i], client_num_status_buf, BUFFER_SIZE, 0) )) == -1)
            {
                printf("all_send失败\n");
                perror("send");
                exit(1);
            }

        }
    }
}
//向全部客户端发送来自服务器的消息
void send_all_buf(char *buf_server)
{
    for(int i=0;i<MAX_QUE_CONN_NM;i++)
    {
        if(accept_status[i]==1)
        {
            if((sendbytes = (send(client_fd[i], buf_server, BUFFER_SIZE, 0) )) == -1)
            {
                printf("all_send失败\n");
                perror("send");
                exit(1);
            }

        }
    }
}
/*服务器在准备的时候会进行读取题目和保存答案*/
int read_test_status()
{
    FILE *filefd; //文件标准符号

    if((filefd = fopen(File_src, "r")) == NULL )
    {
        /*读取文本失败*/
        return 1;
    }

    while(!feof(filefd))
    {
        
        fgets(problem[problem_num], BUFFER_SIZE -1, filefd);

        //answer 等于 问题的最后一位
        //fgets 会读入最后的回车符 '\n' 显示的最后一位要减去3
        answer[problem_num] = problem[problem_num][strlen(problem[problem_num])-3];

        //题目要去除最后的答案
       for(int i=strlen(problem[problem_num])-3; i<strlen(problem[problem_num]); i++)
       {
           problem[problem_num][i] = 0;
       }

       //获取的题目数量
        problem_num++;
    }
    return 0;
}


/*主函数*/
int main()
{
    /* 打开系统日志服务，openlog */
    openlog("test_syslog", LOG_PID, LOG_DAEMON);
    thread_num = 0;//线程数量
    want_test_num = 0;
    testing_num = 0;
    /*建立sockfd*/
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket");
        syslog(LOG_ERR, "socket");
        exit(1);
    }
    printf("Socket: id = %d\n", sockfd);

    /*设置socket_in 的参数*/
    server_sockaddr.sin_family = AF_INET; // ipv4
    server_sockaddr.sin_port = htons(PORT);
    server_sockaddr.sin_addr.s_addr = INADDR_ANY; //监听0.0.0.0 全部网卡地址

    memset(&(server_sockaddr.sin_zero), 0, 8);

    int i =1;
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));

    /*绑定bind()*/
    if(bind(sockfd, (struct sockaddr *)&server_sockaddr, sizeof(struct sockaddr)) == -1)
    {
        perror("bind");
        syslog(LOG_ERR, "bind");
        exit(1);
    }
    printf("Bind success!\n");

    /*listen 监听函数*/
    if(listen(sockfd, MAX_QUE_CONN_NM) == -1)
    {
        perror("listen");
        syslog(LOG_ERR, "listen");
        exit(1);
    }
    printf("Listen.....\n");

    /*客户端现在的连接个数*/
    accept_num = 0;
    pthread_t accept_thread;
    int accept_res;// accept线程标识符
    accept_res = pthread_create(&accept_thread, NULL, thread_accpt, (void*)0);
    if(accept_res != 0)
    {
        perror("pthread");
        syslog(LOG_ERR, "pthread");
        exit(1);
    } 
    /*
    sin_size = sizeof(client_sockaddr[0]);
    if((client_fd = accept(sockfd, (struct sockaddr *)&client_sockaddr, &sin_size))==-1)
    {
        perror("accept");
        exit(1);
    }
    *?

    /*服务器调用读取题目函数 成功返回 0*/
    if(read_test_status() == 0)
    {
        printf("题目准备完毕可以发题\n");
    }
    else /**/
    {
        printf("无法读取题目，无法考试\n");
        exit(1);
    }
    
    for(int i=0; i<problem_num; i++)
    {
        printf("%s\n",problem[i]);
        printf("%c\n",answer[i]);
    }

    pthread_t scanf_thread;
    int res; //线程表示符
    res = pthread_create(&scanf_thread, NULL, send_scanf, (void*)0);
    if(res != 0)
    {
        perror("pthread");
        printf("创建接受信息线程失败\n");
        exit(1);
    }
   /*
   int a=0,b=0;
    while (1)
    {
       if(strncmp(get_buf, "AA", 2) == 0)
       {
            memset(buf, 0, strlen(buf));
            strcpy(buf,problem[a]);
            if((sendbytes = (send(client_fd[0], buf, BUFFER_SIZE, 0) )) == -1)
            {
                perror("send");
                exit(1);
            }
            a++;
            memset(buf, 0, strlen(buf));
       }
       else if(strncmp(get_buf, "BB", 2) == 0)
       {
            memset(buf, 0, strlen(buf));
            strcpy(buf,problem[b]);
            if((sendbytes = (send(client_fd[1], buf, BUFFER_SIZE, 0) )) == -1)
            {
                perror("send");
                exit(1);
            }
            b++;
            memset(buf, 0, strlen(buf));
       }
        sleep(1);
    }
    */
    while(1)
    {
        if(exit_status == 1)
        {
            break;
        }
        sleep(1);
    } 
    closelog();
    close(sockfd);
    exit(0);

}
